/*******************************************************************************
 * Copyright (c) 2008 Sylvain Sicard.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser Public License v2.1
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * 
 * Contributors:
 *     Sylvain Sicard - initial API and implementation
 ******************************************************************************/
/**
 * Feb 21, 2008
 * ssicard
 */
package fr.jade.fraclite.orb;

import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Map;

import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.type.InterfaceType;
import org.objectweb.naming.Binder;
import org.objectweb.naming.Name;
import org.objectweb.naming.NamingException;

import fr.jade.fraclite.api.ComponentInterface;
import fr.jade.fraclite.orb.names.AbstractName;
import fr.jade.fraclite.orb.names.TcpIpName;

/**
 * @author ssicard
 *
 */
public class RMIBinder implements Binder {

  ClassLoader cl = null;
  
  String host = null;
  
  Map<Long, Skeleton> exportedInterfaces;
  Map<Name, Object> localInterfaces;
  
  StubFactory sf = null;
  
  public RMIBinder() throws NamingException{
    cl = getClass().getClassLoader();
    try {
      host = InetAddress.getLocalHost().getCanonicalHostName();
    } catch (UnknownHostException e) {
      throw new NamingException("Cannot create RMIBinder "+e.toString() );
    }
    sf = new StubFactory(cl);
    exportedInterfaces = new HashMap<Long, Skeleton>();
    localInterfaces = new HashMap<Name, Object>();
  }
  
  public Object bind(Name n, Object hints) throws NamingException {
    if(localInterfaces.containsKey(n)){
      System.out.println("[RMIBinder] optimize local reference");
      return localInterfaces.get(n);
    }
    assert n instanceof AbstractName : "name must be instance of AbstractName";
    String signature = ((AbstractName)n).getSignature();
    Class<?> itf;
    try {
      itf = cl.loadClass(signature);
    } catch (ClassNotFoundException e) {
      throw new NamingException("Cannot bind name "+e.toString() );
    }
    Class<?>[] itfs = new Class[]{itf, ComponentInterface.class, Name.class};
    return sf.newStub(this, itfs, n, signature);
  }

  public Name decode(byte[] b) throws NamingException {
    return new TcpIpName(b, this);
  }

  public Name export(Object o, Object hints) throws NamingException {
    assert hints == null || hints instanceof Map : "hints must be instance of Map";
    assert o instanceof Interface : "Cannot export an object which is not instance of Interface";
    Interface itf = (Interface)o;
    Skeleton skel = null;
    int port=0;
    if(exportedInterfaces.containsKey(itf.getFcItfGUId())){
      skel = exportedInterfaces.get(itf.getFcItfGUId());
    }else{
      try {
        skel = (Skeleton)sf.newSkeleton((Map)hints, itf, this);
      } catch (IOException e) {
        throw new NamingException("Cannot create skeleton "+e.toString() );
      }
      exportedInterfaces.put(itf.getFcItfGUId(), skel);
    }
    port = skel.getPort();
    long itfId = itf.getFcItfGUId();
    String signature = ((InterfaceType)itf.getFcItfType()).getFcItfSignature();
    Name n = new TcpIpName(itfId, signature, this, host, port);
    localInterfaces.put(n, o);
    return n;
  }

}
